package com.bjsasc.open.mqtt.controller;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.bjsasc.framework.core.id.util.IDHelper;
import com.bjsasc.framework.util.BFUtils;
import com.bjsasc.open.mqtt.result.JsonResult;
import com.bjsasc.open.mqtt.result.ResultGenerator;
import com.bjsasc.open.util.AESUtil;
import com.bjsasc.open.util.DateUtils;
import com.bjsasc.open.util.StringUtil;


/** 
 * @ClassName: FfmpegCommandController 
 * @Description: 视频监控服务：  完成视频实时预览、 视频回放、视频截图 
 * @author zhangbingbing 
 * @date 2024年3月25日 上午11:16:47 
 * @version 1.0 
 */
@RestController
@RequestMapping(value = "/video/ffmpeg")
public class FfmpegCommandController {
	
	private static final Logger log = LoggerFactory.getLogger(FfmpegCommandController.class);
	
	 /**
	  *   rtmp的服务器ip
	 */
	 @Value("${rtmp.server.ip}")
	 private String RTMP_SERVER_IP; 
	 
	 /**
	  *   nginx的监听端口
	 */
	 @Value("${nginx.server.port}")
	 private Integer NGINX_SERVER_PORT; 
	 
	 /**
	  *   nginx的请求方式
	 */
	 @Value("${nginx.server.request.mode}")
	 private String REQUEST_MODE;
	 
	 /**
	  *   ffmpeg转流脚本所在的路径
	 */
	 @Value("${rtmp.shell.path}")
	 private String RTMP_SHELL_PATH; 
	 
	 /**
	  *   实时流转为mp4后所在的路径
	 */
	 @Value("${mp4.path}")
	 private String MP4_PATH;	 
	 
	 /**
	  *   NVR设备的登录用户名
	 */
	 @Value("${rtsp.user}")
	 private String RTSP_USER;
	 
	 /**
	  *   NVR设备的登录密码
	 */
	 @Value("${rtsp.password}")
	 private String RTSP_PASSWORD; 
	 
	 /**
	  *   NVR设备的登录ip
	 */
	 @Value("${rtsp.url}")
	 private String RTSP_URL; 
	 
	 /**
	  *   NVR设备的默认端口号
	 */
	 @Value("${rtsp.default.port}")
	 private String RTSP_DEFAULT_PORT;	 
	
	@Autowired
	private StringRedisTemplate stringRedisTemplate;	 
	 
	private StringUtil stringUtil;
	
    /**
     * 请求入参是一个实体,并且加上了 @RequestBody
     * 一般适用于前端Header中Content-Type 为 application/json的场景
     * 注意入参要是json格式
     * 
     *   用于获取实时预览的url
     * @param json
     * @return JsonResult
     */
    @PostMapping("/getrealplayurl")
    public JsonResult getrealplayurl(@RequestBody JSONObject json){
    	
    	JSONObject data = new JSONObject();
    	try {
    		log.info("传参为:" + json.toJSONString());
    		//摄像头通道号
    		String channelNum = json.getString("channelNum");
    		if(StringUtils.isEmpty(channelNum)) {
    			return ResultGenerator.getFailResult("摄像头通道号为空，不能转流");
    		}
    		
    		//密码解密处理
    		String password = AESUtil.decrypt(RTSP_PASSWORD, AESUtil.KEY, AESUtil.VIPARA);
    		
//    		rtsp://admin:bjsasc12@10.0.36.205:554/Streaming/Channels/101
    		String rtsp_url = "rtsp://" + RTSP_USER + ":" + password + "@" + RTSP_URL +
    				                   ":" + RTSP_DEFAULT_PORT + "/Streaming/Channels/" + channelNum + "01";
    		
    		log.info("摄像头的rtsp播放地址为: " + rtsp_url);
    		String uuid = IDHelper.uuid();
//    		rtmp://127.0.0.1:1935/live/4
    		String rtmp_url = "rtmp://127.0.0.1:1935/live/" + uuid;
    		log.info("转流之后rtmp的播放地址为: " + rtmp_url);
    		
        	String http_url = REQUEST_MODE + "://" + RTMP_SERVER_IP +":" + NGINX_SERVER_PORT + "/live?port=1935&app=live&stream=" + uuid;
        	log.info("转流之后https的播放地址为: " + http_url);
    		
        	//执行ffmpeg将rtsp实时流转为rtmp
		    ProcessBuilder pb = new ProcessBuilder(RTMP_SHELL_PATH+"ffmpeg-command.sh", "start",rtsp_url," " + rtmp_url);
		    Process process = pb.start();
        	
			 InputStreamReader ir=new InputStreamReader(process.getInputStream());
			 LineNumberReader input = new LineNumberReader (ir);
			 String line = null;
			 String pid = null;
			 while ((line = input.readLine ()) != null){
				 if(BFUtils.isNumber(line)){
					 pid = line;
					 log.info("转流进程号为:" + line);
				        
				 } else {
					 log.info("获取的值为:" + line);
				 }
			  	}
			 
			 if(StringUtils.isEmpty(pid)) {
				 
				 return ResultGenerator.getFailResult("转流进程无法启动，不能播放");
			 } else {
				 data.put("httpurl", http_url);
				 data.put("pid", pid);
				 
				//将所有启动起来的转流进程信息存储到redis中
             	SetOperations<String, String> set = stringRedisTemplate.opsForSet();
             	set.add(stringUtil.RTSP_TO_RTMP, pid);
				 
             	//每一个进程号作为String类型的key，设置它的有效期
    			stringRedisTemplate.opsForValue().set(stringUtil.FLV_HTTP + pid, pid,stringUtil.effective_time, TimeUnit.MINUTES); 
			 }
    		
    	}catch(Exception ex) {
    		log.error("实时转流发生异常,原因为:" + ex.toString());
    		return ResultGenerator.getFailResult("转流发生了异常");
    	}
    	
        return ResultGenerator.getSuccessResult(data);
    }

    //关闭转流进程
    @GetMapping("/killprocess")
    public JsonResult killProcess(String pid) {
    	JSONObject data = new JSONObject();
    	try {
    		log.info("关闭转流进程传参为:" + pid);
    		if(StringUtils.isEmpty(pid)) {
    			return ResultGenerator.getFailResult("pid字段为空");
    		}
    		
			 String strCommand="kill -9 "+pid;
			 Process process = Runtime.getRuntime().exec(strCommand);
			 InputStreamReader ir=new InputStreamReader(process.getInputStream());
			 LineNumberReader input = new LineNumberReader (ir);
			 String line;
			 while ((line = input.readLine ()) != null){
				 log.info("结果为:" + line);
			  }
			 
			 //将该进程号对应的String类型的key也删除掉
			 Boolean deleteResult = stringRedisTemplate.delete(stringUtil.FLV_HTTP + pid);
			 log.info("从redis中删除进程号的结果为:" + deleteResult);
			 
			//将该进程号从Set集合中删除
			 stringRedisTemplate.opsForSet().remove(stringUtil.RTSP_TO_RTMP, pid);
			
    	}catch(Exception ex) {
    		log.error("关闭转流进程发生异常,原因为:" + ex.toString());
    		return ResultGenerator.getFailResult("关闭转流进程发生异常");
    	}
    	
        return ResultGenerator.getSuccessResult(data);
    
    }
    
    //发送转流进程存在的心跳
    @GetMapping("/sendheartbeat")
    public JsonResult sendHeartBeat(String pid) {
    	try {
    		log.info("心跳进程为:" + pid);
    		if(StringUtils.isEmpty(pid)) {
    			return ResultGenerator.getFailResult("pid字段为空,不能发送心跳");
    		}
    		
         	//每一个进程号作为String类型的key，设置它的有效期为20分钟
    		String value = stringRedisTemplate.opsForValue().get(stringUtil.FLV_HTTP + pid);
    		if(StringUtils.isEmpty(value)) {
    			log.info("该进程在redis中不存在");
    		} else {
    			this.stringRedisTemplate.expire(stringUtil.FLV_HTTP + pid, stringUtil.effective_time, TimeUnit.MINUTES);//以分为单位
    		}
    		
    	}catch(Exception ex) {
    		log.error("发送心跳异常,原因为:" + ex.toString());
    		return ResultGenerator.getFailResult("发送心跳异常");
    	}
    	
        return ResultGenerator.getSuccessResult();
    
    }
    
    /**
     * 
     *   将rtsp实时视频流转为mp4格式的文件
     *   
     * @param json
     * @return JsonResult
     */
    @PostMapping("/processMP4")
    public JsonResult processMP4(@RequestBody JSONObject json){
    	JSONObject data = new JSONObject();
    	try {
    		log.info("转mp4传参为:" + json.toJSONString());
    		//摄像头通道号
    		String channelNum = json.getString("channelNum");
    		//历史视频内码
    		String lsspnm = IDHelper.uuid();
    		
    		//任务的持续时间
    		Long duration = json.getLong("duration");
    		
    		if(StringUtils.isEmpty(channelNum)) {
    			return ResultGenerator.getFailResult("摄像头通道号为空，不能转流");
    		}
    		
    		if(duration == null) {
    			duration = 600L; //默认10分钟
    		}
    		
    		log.info("任务持续时间为:" + duration);
    		
    		String path ="";
    		
    		String currentdate = DateUtils.formatDate(new Date(),"yyyyMMdd");
    		
            if(MP4_PATH.endsWith("/") || MP4_PATH.endsWith("\\")){
                path = MP4_PATH + currentdate;
            }else{
                path = MP4_PATH+"/"+ currentdate;
            }
			
			File f = new File(path);
            if(!f.exists()){
            	f.mkdirs();
            }
    		
    		log.info("根据日期生成的路径为:"+ path);
    		
            String mp4path = path  +"/"+ lsspnm + ".mp4";
            log.info("最终生成的mp4路径为： " + mp4path);
            
            String password = AESUtil.decrypt(RTSP_PASSWORD, AESUtil.KEY, AESUtil.VIPARA);
    		
//    		rtsp://admin:bjsasc12@10.0.36.205:554/Streaming/Channels/101
    		String rtsp_url = "rtsp://" + RTSP_USER + ":" + password + "@" + RTSP_URL +
    				                   ":" + RTSP_DEFAULT_PORT + "/Streaming/Channels/" + channelNum + "01";
    		
    		log.info("摄像头的rtsp播放地址为: " + rtsp_url);

        	//执行ffmpeg将rtsp实时流转为mp4
		    ProcessBuilder pb = new ProcessBuilder(RTMP_SHELL_PATH+"ffmpeg-command-mp4.sh", "start",rtsp_url," " + mp4path);
		    Process process = pb.start();
        	
			 InputStreamReader ir=new InputStreamReader(process.getInputStream());
			 LineNumberReader input = new LineNumberReader (ir);
			 String line = null;
			 String pid = null;
			 while ((line = input.readLine ()) != null){
				 if(BFUtils.isNumber(line)){
					 pid = line;
					 log.info("转mp4文件进程号为:" + line);
				        
				 } else {
					 log.info("获取的值为:" + line);
				 }
			  	}
			 
			 if(StringUtils.isEmpty(pid)) {
				 
				 return ResultGenerator.getFailResult("转流mp4进程无法启动");
			 } else {
				 data.put("lsspnm", lsspnm);
				 data.put("wjlj", currentdate);
				 data.put("pid", pid);
				 
				// 将所有启动起来的转流进程信息存储到redis中
				SetOperations<String, String> set = stringRedisTemplate.opsForSet();
				set.add(stringUtil.RTSP_TO_MP4_SET, pid);
				 
				//每一个进程号作为String类型的key，设置它的有效期
				stringRedisTemplate.opsForValue().set(stringUtil.MP4_STRING + pid, pid); 
				this.stringRedisTemplate.expire(stringUtil.MP4_STRING + pid, duration, TimeUnit.SECONDS);//以秒为单位
			 }
			 
			 //ffmpeg-截图功能实现
	          String jpgpath = path  +"/"+ lsspnm + ".jpg";
	          log.info("最终生成的图片路径为： " + jpgpath);
			 
			 pb = new ProcessBuilder(RTMP_SHELL_PATH+"ffmpeg-command-jpg.sh", "start",rtsp_url," " + jpgpath);
			 pb.start(); 
			 
    	}catch(Exception ex) {
    		log.error("实时流转mp4发生了异常,原因为:" + ex.toString());
    		return ResultGenerator.getFailResult("实时流转mp4发生了异常");
    	}
    	
        return ResultGenerator.getSuccessResult(data);
    }
    
    //关闭rtsp流转MP4进程
    @GetMapping("/killProcessMP4")
    public JsonResult killProcessMP4(String pid) {
    	JSONObject data = new JSONObject();
    	try {
    		log.info("转mp4传参为:" + pid);
    		if(StringUtils.isEmpty(pid)) {
    			return ResultGenerator.getFailResult("pid字段为空");
    		}
    		
    		//优雅地关闭进程
			 String strCommand="kill -15 "+pid;
			 Process process = Runtime.getRuntime().exec(strCommand);
			 
			 //将该进程号对应的String类型的key也删除掉
			 Boolean deleteResult = stringRedisTemplate.delete(stringUtil.MP4_STRING + pid);
			 log.info("从redis中删除进程号的结果为:" + deleteResult);
				
			 //将该进程号从Set集合中删除
			 stringRedisTemplate.opsForSet().remove(stringUtil.RTSP_TO_MP4_SET, pid);
			
    	}catch(Exception ex) {
    		log.error("关闭转流进程发生异常,原因为:" + ex.toString());
    		return ResultGenerator.getFailResult("关闭转流进程发生异常");
    	}
    	
        return ResultGenerator.getSuccessResult(data);
    
    }
    
	/**
	 * 下载单个文件
	 * @param wjlj
	 * @param wjmc
	 * @param wjgs
	 */
    @GetMapping("/downloadFile")
	public  void downloadFile(HttpServletRequest request,HttpServletResponse response,String wjlj,String wjmc,String wjgs) {
		try { 
			log.info("下载视频文件参数为:{},{},{}",wjlj,wjmc,wjgs);
			if(StringUtils.isEmpty(wjlj) || StringUtils.isEmpty(wjmc) || StringUtils.isEmpty(wjgs)){
				return;
			}
			
			String filename = "";
			String filepath = "";
			
			//获取文件名称
			if("mp4".equalsIgnoreCase(wjgs)) {
				filename = wjmc + ".mp4";
			} else if("jpg".equalsIgnoreCase(wjgs)){
				filename = wjmc + ".jpg";
			} else{
				log.info("文件格式不支持");
				return;
			}
			
			log.info("待下载的文件名称为:{}",filename);

			//获取文件路径
            if(MP4_PATH.endsWith("/") || MP4_PATH.endsWith("\\")){
            	filepath = MP4_PATH + wjlj + "/"+ filename;
            }else{
            	filepath = MP4_PATH + "/" +wjlj  + "/" + filename;
            }
            
            log.info("待下载的文件路径为:{}",filepath);
			
			File f = new File(filepath);
            if(!f.exists()){
            	log.info("文件不存在");
            	response.sendError(404, "File not found!");
            	return;
            }
			
			//优化后的方式
			BufferedInputStream br = new BufferedInputStream(new FileInputStream(f));
			byte[] bs = new byte[1024];
			int len = 0;
			response.reset();//非常重要
			
            if((StringUtils.isEmpty(""))){//在线预览
            	URL u = new URL("file:///" + filepath);
                response.setContentType(u.openConnection().getContentType());
                response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(filename, "UTF-8"));
            } else {//纯下载
            	response.setContentType("application/x-msdownload");
                response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
            }
            
            ServletOutputStream out = response.getOutputStream();
			while ((len = br.read(bs)) > 0) {
				out.write(bs, 0, len);
			}
			br.close();
			out.close();
			
		} catch (Exception ex) {
			log.error("下载失败:" + ex.toString());
		}
		
	}       
   
}
